#include "board_ex.h"

#include "hpm_gpio_drv.h"
#include "hpm_tsns_drv.h"
#include "hpm_ppor_drv.h"
#include "hpm_gptmr_drv.h"
#include "hpm_owr_drv.h"

#include "FreeRTOS.h"
#include "task.h"

#include "ecatslv.h"

#define OTP_CHIP_UID_IDX_START  (8U)
#define OTP_CHIP_UID_IDX_END    (11U)

/* Global variables */
volatile uint32_t g_freertos_run_time_counter = 0;
volatile uint64_t g_up_time_100us_counter = 0;

/* Static Variable Definition*/
static struct device_info s_device_info = { 
    .device_name = "HPM5E-EC-DEV", 
    .uid = "?",
    .firmware_version = (1 << 16) | (2 << 8) | (0),
    .up_time = 0, 
    .cpu_usage = 0,
    .memory_usage = 0,
    .temperature = 0,
};
static owr_config_t s_owr_config;
static uint32_t s_ds18b20_resolution;

static void on_timer(void)
{
  g_freertos_run_time_counter++;
  g_up_time_100us_counter++;
}

static float get_cpu_usage_percent(void) 
{
    static uint32_t lastTotalRunTime = 0;
    static uint32_t lastIdleTime = 0;
    static uint64_t lastCallTimeMs = 0;
    static bool firstCall = true;

    TaskStatus_t *pxTaskStatusArray;
    uint32_t      ulTotalRunTime;
    uint32_t      ulIdleTime = 0;
    UBaseType_t   uxArraySize, uxTask;
    float         cpuUsage = 0.0f;

    uxArraySize = uxTaskGetNumberOfTasks();
    pxTaskStatusArray = (TaskStatus_t *)pvPortMalloc(uxArraySize * sizeof(TaskStatus_t));
    if (pxTaskStatusArray == NULL) return 0;

    uxArraySize = uxTaskGetSystemState(pxTaskStatusArray,
                                       uxArraySize,
                                       &ulTotalRunTime); 

    if (ulTotalRunTime > 0) {
        for (uxTask = 0; uxTask < uxArraySize; uxTask++) {
            if (pxTaskStatusArray[uxTask].xHandle == xTaskGetIdleTaskHandle()) {
                ulIdleTime = pxTaskStatusArray[uxTask].ulRunTimeCounter;
                break;
            }
        }
    }

    vPortFree(pxTaskStatusArray);

    if (!firstCall) 
    {
        uint32_t deltaTotal = ulTotalRunTime - lastTotalRunTime;
        uint32_t deltaIdle  = ulIdleTime - lastIdleTime;

        if (deltaTotal > 0) 
        {
            cpuUsage = 100.0f - ((float)deltaIdle * 100.0f / (float)deltaTotal);
        }
    } 
    else 
    {
        firstCall = false;
    }

    // cpu使用率统计周期2s
    if (g_up_time_100us_counter / 10 - lastCallTimeMs > 2000)
    {
        lastTotalRunTime = ulTotalRunTime;
        lastIdleTime = ulIdleTime;
        lastCallTimeMs = g_up_time_100us_counter / 10;
    }

    return cpuUsage;
}


#define DS18B20_SCRATCHPAD_MAX_COUNT (9U)
#define DS18B20_SCRATCHPAD_CONFIG_IDX (4U)
#define DS18B20_SCRATCHPAD_TEMP_LSB_IDX (0U)
#define DS18B20_SCRATCHPAD_TEMP_MSB_IDX (1U)
#define DS1B820_ROM_CODE_RES_COUNT (8U)

#define DS18B20_COMMAND_READ_ROM (0x33)
#define DS18B20_COMMAND_SKIP_ROM (0xcc)
#define DS18B20_COMMAND_READ_SCRATCHPAD (0xbe)
#define DS18B20_COMMAND_CONVERT_TEMP (0x44)

#define DS18B20_TEMP_RES_9BIT  (0.5f)
#define DS18B20_TEMP_RES_10BIT (0.25f)
#define DS18B20_TEMP_RES_11BIT (0.125f)
#define DS18B20_TEMP_RES_12BIT (0.0625f)

#pragma pack(1)
typedef struct {
  uint8_t family_code;
  uint8_t serial_num[6];
  uint8_t crc;
} ds18b20_read_rom_result_t;

typedef enum {
    ds18b20_thermometer_resolution_9bit = 0,
    ds18b20_thermometer_resolution_10bit,
    ds18b20_thermometer_resolution_11bit,
    ds18b20_thermometer_resolution_12bit
} ds18b20_thermometer_resolution_t;

hpm_stat_t ds18b20_skip_rom(void)
{
    /* skip ROM command */
    owr_write_data(BOARD_OWR, DS18B20_COMMAND_SKIP_ROM);

    return status_success;
}

hpm_stat_t ds18b20_read_rom(void)
{
    ds18b20_read_rom_result_t read_rom_result;
    uint8_t *p = (uint8_t *)&read_rom_result;

    /* read ROM command */
    owr_write_data(BOARD_OWR, DS18B20_COMMAND_READ_ROM);

    /* Read result */
    for (int i = 0; i < 8; i++) {
        /* read Data */
        owr_read_data(BOARD_OWR, (uint32_t *)p++);
    }

    printf("Family Code: %02x\n", read_rom_result.family_code);

    printf("Serial Num:");
    for (int i = 0; i < 6; i++) {
        printf("%02x ", read_rom_result.serial_num[i]);
    }
    printf("\n");

    printf("CRC: %02x\n", read_rom_result.crc);

    return status_success;
}

hpm_stat_t ds18b20_read_scratchpad(uint32_t *data)
{
    uint32_t *p = NULL;

    if (data == NULL) {
        return status_invalid_argument;
    }

    p = data;

    /* read ROM command */
    owr_write_data(BOARD_OWR, DS18B20_COMMAND_READ_SCRATCHPAD);

     /* read result */
    for (uint8_t i = 0; i < DS18B20_SCRATCHPAD_MAX_COUNT; i++) {
        /* read Data */
        owr_read_data(BOARD_OWR, p++);
    }

    return status_success;
}

hpm_stat_t ds18b20_read_config(uint32_t *data)
{
    uint32_t scratchpad[DS18B20_SCRATCHPAD_MAX_COUNT];
    uint8_t res;

    if (data == NULL) {
        return status_invalid_argument;
    }

    ds18b20_read_scratchpad(scratchpad);

    res = ((scratchpad[DS18B20_SCRATCHPAD_CONFIG_IDX] & (BIT6_MASK | BIT5_MASK)) >> 5);

    if (res == ds18b20_thermometer_resolution_12bit) {
        printf("Thermometer resolution: 12 bit\n");
    } else {
        printf("The current resolution is not the factory default value: %d!\n", res);
    }

    *data = res;

    return status_success;
}

hpm_stat_t ds18b20_calc_temp(int16_t data, uint8_t res, float *temp)
{
    float res_temp[] = {DS18B20_TEMP_RES_9BIT, DS18B20_TEMP_RES_10BIT, DS18B20_TEMP_RES_11BIT, DS18B20_TEMP_RES_12BIT};

    *temp = data * res_temp[res];

    return status_success;
}

hpm_stat_t ds18b20_read_temp(uint8_t *data)
{
    uint32_t scratchpad[DS18B20_SCRATCHPAD_MAX_COUNT];
    uint8_t temp_lsb, temp_msb;

    ds18b20_read_scratchpad(scratchpad);

    temp_lsb = scratchpad[DS18B20_SCRATCHPAD_TEMP_LSB_IDX];
    temp_msb = scratchpad[DS18B20_SCRATCHPAD_TEMP_MSB_IDX];

    *data++ = temp_lsb;
    *data   = temp_msb;

    return status_success;
}

hpm_stat_t ds18b20_convert_temp(void)
{
    uint32_t data;
    uint32_t timeout = 8; /* 800ms */

    /* convert temperature command */
    owr_write_data(BOARD_OWR, DS18B20_COMMAND_CONVERT_TEMP);

    while (timeout--) {
        vTaskDelay(100);
        owr_read_data(BOARD_OWR, &data);
        if (data) {
            break;
        }
    }

    if (timeout == 0) {
        return status_timeout;
    }

    return status_success;
}
#pragma pack()

void board_init2(void)
{
    /* init tsns */
    clock_add_to_group(clock_tsns, 0);
    tsns_enable_continuous_mode(HPM_TSNS);
    tsns_enable(HPM_TSNS);

    /* init owr ds18b20 */
    {
      uint32_t status;

      /* clock initialization */
      board_init_owr_clock(BOARD_OWR);

      /* pin initialization */
      board_init_owr_pins(BOARD_OWR);

      /* software reset */
      owr_sw_reset(BOARD_OWR);

      /* set config */
      s_owr_config.clock_source_frequency = BOARD_OWR_CLK;

      /* OWR initialization */
      owr_init(BOARD_OWR, &s_owr_config);

      /* rpp */
      owr_reset_and_presence_pulses(BOARD_OWR);

      /* check if OWR slave is existent */
      owr_get_presence_status(BOARD_OWR, &status);

      if (status) 
      {
          printf("OWR Slave Detected!\n");

          owr_reset_and_presence_pulses(BOARD_OWR);
          ds18b20_read_rom();

          owr_reset_and_presence_pulses(BOARD_OWR);
          ds18b20_skip_rom();
          ds18b20_read_config(&s_ds18b20_resolution);
      } 
      else 
      {
          printf("No OWR Slave!\n");
      }
    }

    /* init gptmr0, buzzer */
    clock_add_to_group(clock_gptmr0, 0);
    gpio_set_pin_output_with_initial(BOARD_APP_LED1_CTRL, BOARD_APP_LED1_INDEX, BOARD_APP_LED1_PIN, 0);

    /* init gptmr3, 10KHz */
    board_timer_create_us(100, on_timer);
}

void board_get_device_info(struct device_info* data)
{
    if (s_device_info.uid[0] == '?')
    {
        uint32_t uid_words[4];

        uint32_t word_idx = 0;
        for (uint32_t i = OTP_CHIP_UID_IDX_START; i <= OTP_CHIP_UID_IDX_END; i++) {
            uid_words[word_idx++] = ROM_API_TABLE_ROOT->otp_driver_if->read_from_shadow(i);
        }

        uint8_t *uid_bytes = (uint8_t *)uid_words;
        for (uint32_t i = 0; i < sizeof(uid_words); i++) {
            sprintf(s_device_info.uid + i * 2, "%02x", uid_bytes[i]);
        }   
    }

    s_device_info.up_time = g_up_time_100us_counter / 10 / 1000;

    s_device_info.cpu_usage = get_cpu_usage_percent();

    size_t xFreeHeapSize = xPortGetFreeHeapSize(); 
    size_t xTotalHeapSize = configTOTAL_HEAP_SIZE; 
    float fHeapUsagePercentage = 100.0f * (1.0f - ((float)xFreeHeapSize / (float)xTotalHeapSize));  
    s_device_info.memory_usage = fHeapUsagePercentage;

    s_device_info.temperature = tsns_get_current_temp(HPM_TSNS);

    *data = s_device_info;
}

void board_reboot(void)
{
    ppor_reset_mask_set_source_enable(HPM_PPOR, ppor_reset_software);
    /* reset after 1us */
    ppor_sw_reset(HPM_PPOR, 24);
    while(1) {
    }
}

void board_get_led(struct led *data)
{
    if (data->number == 1)
      data->state = gpio_read_pin(BOARD_APP_LED1_CTRL, BOARD_APP_LED1_INDEX, BOARD_APP_LED1_PIN) != 0 ? true : false;
    else if (data->number == 2)
      data->state = gpio_read_pin(BOARD_APP_LED2_CTRL, BOARD_APP_LED2_INDEX, BOARD_APP_LED2_PIN) != 0 ? true : false;
}

void board_set_led(struct led *data)
{
    if (data->number == 1)
      gpio_write_pin(BOARD_APP_LED1_CTRL, BOARD_APP_LED1_INDEX, BOARD_APP_LED1_PIN, data->state ? 1 : 0);
    else if (data->number == 2)
      gpio_write_pin(BOARD_APP_LED2_CTRL, BOARD_APP_LED2_INDEX, BOARD_APP_LED2_PIN, data->state ? 1 : 0);
}

static struct buzzer s_buzzer;

void board_get_buzzer(struct buzzer *data)
{
  data->frequency = s_buzzer.frequency;
  data->state = s_buzzer.state;
}

void board_set_buzzer(struct buzzer *data)
{
  s_buzzer.frequency = data->frequency;
  s_buzzer.state = data->state;

  if (!s_buzzer.state || s_buzzer.frequency == 0)
  {
    gptmr_update_cmp(HPM_GPTMR0, 3, 0, 0);
    gptmr_stop_counter(HPM_GPTMR0, 3);

    HPM_IOC->PAD[IOC_PAD_PC15].FUNC_CTL = IOC_PC15_FUNC_CTL_GPIO_C_15;
    gpio_set_pin_output_with_initial(BOARD_APP_BUZZER_CTRL, BOARD_APP_BUZZER_INDEX, BOARD_APP_BUZZER_PIN, 0);
  }
  else
  {
    HPM_IOC->PAD[IOC_PAD_PC15].FUNC_CTL = IOC_PC15_FUNC_CTL_GPTMR0_COMP_3;

    gptmr_channel_config_t gptmr_config;
    uint32_t gptmr_freq;
    gptmr_channel_get_default_config(HPM_GPTMR0, &gptmr_config);
    gptmr_freq = clock_get_frequency(clock_gptmr0);              /* 获取GPTMR0的时钟频率 */
    gptmr_config.mode = gptmr_work_mode_no_capture;              /* 无捕获 */
    gptmr_config.reload = gptmr_freq / s_buzzer.frequency;       /* 频率 */
    gptmr_config.cmp[0] = gptmr_config.reload * 0.5;             /* 占空比 */
    gptmr_config.enable_cmp_output = true;                       /* 使能输出比较 */
    gptmr_config.enable_software_sync = false;                   /* 不使能软件同步 */
    gptmr_channel_config(HPM_GPTMR0, 3, &gptmr_config, true);
    gptmr_channel_reset_count(HPM_GPTMR0, 3);
    gptmr_start_counter(HPM_GPTMR0, 3);
  }
}


static double s_de18b20_temperature;

void board_get_ds18b20(struct ds18b20 *data)
{
  data->temperature = s_de18b20_temperature;

  if (s_ds18b20_resolution == 0) data->accuracy = DS18B20_TEMP_RES_9BIT;
  if (s_ds18b20_resolution == 1) data->accuracy = DS18B20_TEMP_RES_10BIT;
  if (s_ds18b20_resolution == 2) data->accuracy = DS18B20_TEMP_RES_11BIT;
  if (s_ds18b20_resolution == 3) data->accuracy = DS18B20_TEMP_RES_12BIT;  
}

void board_update_ds18b20(void)
{
  int16_t temp;
  float temp_value;

  owr_reset_and_presence_pulses(BOARD_OWR);
  ds18b20_skip_rom();

  ds18b20_convert_temp();
  
  owr_reset_and_presence_pulses(BOARD_OWR);
  ds18b20_skip_rom();

  ds18b20_read_temp((uint8_t *)&temp);
  ds18b20_calc_temp(temp, s_ds18b20_resolution, &temp_value);

  s_de18b20_temperature = temp_value;
}

extern uint16_t aPdOutputData[];
extern uint16_t aPdInputData[];

extern volatile uint64_t g_ecat_sync_cycle;

void board_get_ecat_status(struct ecat_status *data)
{
  if (nAlStatus == 1) sprintf(data->slave_state, "INIT"); 
  else if (nAlStatus == 2) sprintf(data->slave_state, "PREOP"); 
  else if (nAlStatus == 4) sprintf(data->slave_state, "SAFEOP"); 
  else if (nAlStatus == 8) sprintf(data->slave_state, "OP"); 
  else sprintf(data->slave_state, "CODE: %d", nAlStatus); 

  if (bEscIntEnabled)
  {
    if (bDcSyncActive)
      sprintf(data->sync_mode, "DC");  
    else
      sprintf(data->sync_mode, "SM");   
    data->sync_period_us = (double) g_ecat_sync_cycle / clock_get_core_clock_ticks_per_us();
  }
  else
  {
    sprintf(data->sync_mode, "FreeRun");   
    data->sync_period_us = (double) g_ecat_sync_cycle / clock_get_core_clock_ticks_per_us();
  }

  data->digital_outputs = aPdOutputData[0];
  data->digital_inputs = aPdInputData[0];
}

void board_get_ecat_input(struct ecat_input *data)
{
  if (aPdInputData[0] & (1 << (data->index)))
    data->state = 1;
  else
    data->state = 0;
}

void board_set_ecat_input(struct ecat_input *data)
{
  if (data->state != 0)
    aPdInputData[0] |= 1 << (data->index);
  else
    aPdInputData[0] &= ~(uint16_t)(1 << (data->index));
}